#+TITLE: GNU GUIX Guide
#+AUTHOR: Zelphir Kaltstahl
#+STARTUP: content indent align inlineimages hideblocks entitiesplain nologdone nologreschedule nologredeadline nologrefile
#+TODO: TODO INPROGRESS | DONE
#+DATE: <2020-10-11 Sun>
#+LANGUAGE: English
#+PRIORITIES: A E E

* Reproducible environments and profiles
** How to install previous package versions

 Writing a ~manifest.scm~ file as follows in itself does not make a basis for a reproducible environment or profile:

 #+begin_src scheme
(specifications->manifest
 '("python@3.8.2"
   "python-redis@3.3.8"))
 #+end_src

 The problem with this approach is, that GNU Guix will not be able to find these specific versions of the packages, when GNU Guix is updated itself. GNU Guix itself is version controlled and with it its references to package sources. The commit of GNU Guix itself needs to be referenced, because different versions of guix contain references to different versions of a package, dropping older versions of packages. It will only know the versions of packages, which come with its precise release. When you update GNU Guix, it will know different versions of packages. It will give an error like the following:

 #+begin_quote
 guix environment: error: python-redis: package not found for version 3.3.8
 #+end_quote

 The same happens, when you try to make an ad-hoc environment as follows:

 #+begin_src shell
guix environment --ad-hoc 'python@3.8' 'python-redis@3.3.8' -- python3.8
 #+end_src

 One could argue, that you should always use the latest version of packages, but that does not make for a reproducible environment.

 To make a truly reproducible environment or profile, which "stands the test of time", one needs to do it differently.

 To track one specific commit of GNU Guix, one needs to create a ~channels.scm~ file. This is done as follows:

 #+begin_src shell
guix describe --format=channels > channels.scm
 #+end_src

 This will write down a definition inside ~channels.scm~, which specifies exactly, which commit id of GNU Guix to track. Such a definition can be used by the ~time-machine~ functionality of GNU Guix in combination with a ~manifest.scm~. Such a ~manifest.scm~ could look as follows:

 #+begin_src scheme
(specifications->manifest
 '("python"
   "python-redis"))
 #+end_src

 Note, that there are no versions specified any longer, as they are already determined by the commit, which GNU Guix shall be on, which is specified in the ~channels.scm~.

** Installing packages from multiple different channels

Tracking only one channel limits package version choice to that one commit of GNU Guix. It is desirable to be able to use package versions of multiple GNU Guix commits.

A ~channels.scm~ file contains a list of channels. For example:

#+begin_src scheme
(list (channel
       (name 'guix)
       (url "https://git.savannah.gnu.org/git/guix.git")
       (commit
        "29a2eb36ff85eb75eeb907aa687fbb655b5f1097")
       (introduction
        (make-channel-introduction
         "9edb3f66fd807b096b48283debdcddccfea34bad"
         (openpgp-fingerprint
          "BBB0 2DDF 2CEA F6A8 0D1D  E643 A2A0 6DF2 A33A 54FA")))))
#+end_src

In this example only one channel is specified in the list. However, it might work to put multiple channels in there, which have versions of packages, which otherwise never appear in the same version of GNU Guix. (TODO: Test this with some example.) See also [[https://guix.gnu.org/manual/en/html_node/Channels.html]] under "Replicating Guix", where an example is given using multiple channels:

#+begin_src scheme
;; Deploy specific commits of my channels of interest.
(list (channel
       (name 'guix)
       (url "https://git.savannah.gnu.org/git/guix.git")
       (commit "d894ab8e9bfabcefa6c49d9ba2e834dd5a73a300"))
      (channel
       (name 'my-personal-packages)
       (url "https://example.org/personal-packages.git")
       (commit "dd3df5e2c8818760a8fc0bd699e55d3b69fef2bb")))
#+end_src

Although this refers to two different Guix repositories. (TODO: Test whether it works for 2 different repositories, if it does not work with 2 channels for only 1 repository.)

** Finding a GNU Guix commit id for packages in a specific version

Currently (at timestamp 2020-10-25) there is no comfortable command line way to do this. To find a commit id of GNU Guix follow the following steps:

1. visit [[https://hpc.guix.info/browse]]
2. enter the package name, for example ~python-redis~
3. click the package name in the results of the search, for example [[https://hpc.guix.info/package/python-redis]]
4. click on "View package version history", for example leading to [[http://data.guix.gnu.org/repository/1/branch/master/package/python-redis]]

   on the page shown you can see the versions of the package you searched. for example for ~python-redis~ one can see: ~2.10.6~, ~3.2.0~, ~3.2.1~, ~3.3.8~, and ~3.5.3~ (at timestamp 2020-10-25). for each version the earliest and latest date when GNU Guix had this version is shown.

5. click on the latest date of for the version you want

   the dates link actually to a page displaying information about GNU Guix at that time, which means at one specific commit. from this page you can extract the so called "revision".

6. use the revision to specify a Guix commit using the following command:

   #+begin_src shell
   guix time-machine --commit=a95057ccee738adedc7207c624b976b50c1ae438 -- environment --ad-hoc python@3.8.2 python-redis@3.5.3 -- python3.8
   #+end_src

   This should be a reproducible environment and work wherever GNU Guix and the required build tools are available.

You can also put that commit of Guix in a ~channels.scm~ file by using the following command:

#+begin_src shell
guix time-machine --commit=a95057ccee738adedc7207c624b976b50c1ae438 -- describe --format=channels > channels.scm
#+end_src

The Guix time machine command is very elegant in its usage, letting you simply specify a command to run with a specific version of Guix by appending ~-- <other command> <arguments of other command>~.

* Calculate Guix hash

** for tarballs

#+begin_src shell
guix hash the-file
#+end_src

** for repositories

#+begin_src shell
guix hash --exclude-vcs --recursive .
#+end_src

* Importing packages

** PyPI

Versions numbers are specified as follows:

#+begin_src shell
guix import pypi jupyterlab_server/2.0.0rc1
#+end_src

Other importers might use different characters to delimit versions.

** Native inputs vs propagated inputs

For python packages it doesn't matter at build time, it's more about if
it's needed at build time (native-inputs) or if it's needed to run the
python program (propagated-inputs) since we don't have a good way to
tell python to keep a reference to other python packages.

Assuming WorkspacesAPITest does actually have an attribute named
lab_config then you'll want to add the installed package to the python
path during the check phase. There are a number of packages which do
this. I can never remember the syntax so I always grep for
'add-installed-pythonpath' when it comes up.
* Debugging
** Looking at logs

You can use the following script to look at logs:

#+begin_src shell
#!/usr/bin/env bash

set -Eeuxo pipefail

# current script directory
DIR="$(cd "$(dirname "$0")" > /dev/null 2>&1; pwd -P)"
printf "script directory: %s\n" "${DIR}"

if [ -z ${1+x} ]; then
    printf "%s\n" "No log file specified.";
    exit 1;
else
    printf "%s %s %s\n" '${1}' 'is set to' "${1}";
    COMPRESSED_LOG_FILE="${1}"
fi

COMPRESSED_FILE_NAME="$(basename -- ${COMPRESSED_LOG_FILE})"
# DECOMPRESSED_FILE_NAME=$(echo "${COMPRESSED_FILE_NAME}" | rev | cut --fields '2-' --delimiter '.' | rev)
DECOMPRESSED_FILE_NAME="${COMPRESSED_FILE_NAME%.*}"

cp --verbose "${COMPRESSED_LOG_FILE}" "${DIR}/${COMPRESSED_FILE_NAME}"
# z8qdlxa1c3bjdcx91p200vgxdkwfyg-python-jupyterlab-server-2.0.0rc1.drv.bz2 ->
# z8qdlxa1c3bjdcx91p200vgxdkwfyg-python-jupyterlab-server-2.0.0rc1.drv
bzip2 --decompress "${COMPRESSED_FILE_NAME}"

cat "${DECOMPRESSED_FILE_NAME}"
rm --verbose "${DECOMPRESSED_FILE_NAME}"
#+end_src
